/*
 * CC3MeshNode.m
 *
 * cocos3d 0.6.1
 * Author: Bill Hollings
 * Copyright (c) 2010-2011 The Brenwill Workshop Ltd. All rights reserved.
 * http://www.brenwill.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * http://en.wikipedia.org/wiki/MIT_License
 * 
 * See header file CC3MeshNode.h for full API documentation.
 */

#include "CC3MeshNode.h"
#include "CC3BoundingVolumes.h"
#include "CC3OpenGLES11Engine.h"
#include "CCPointExtension.h"
#include "CC3VertexArrayMesh.h"
#include "CCGeometry.h"
#include "ccTypes.h"

CC3MeshNode::CC3MeshNode() :
	mesh(NULL),
	material(NULL)
{
}

CC3MeshNode::~CC3MeshNode()
{
	mesh->release();
	material->release();
}

CC3Mesh* CC3MeshNode::getMesh()
{
	return mesh;
}

void CC3MeshNode::setMesh(CC3Mesh* aMesh)
{
	CC3Mesh* oldMesh = mesh;
	mesh = aMesh;
	mesh->retain();
	if (oldMesh) 
	{
		oldMesh->release();
	}
	if (boundingVolume) 
	{
		getBoundingVolume()->buildVolume();
	} 
	else 
	{
		setBoundingVolume(mesh->getDefaultBoundingVolume());
	}
}

CC3MeshModel* CC3MeshNode::getMeshModel()
{
	return (CC3MeshModel*)getMesh();
}

void CC3MeshNode::setMeshModel(CC3MeshModel* newValue)
{
	setMesh(newValue);
}

CC3Material* CC3MeshNode::getMaterial()
{
	return material;
}

void CC3MeshNode::setMaterial(CC3Material* newValue)
{
	CC3Material* oldMaterial = material;
	material = newValue;
	if (material) 
	{
		material->retain();
	}
	if (oldMaterial) 
	{
		oldMaterial->release();
	}
}

cocos2d::ccColor4F CC3MeshNode::getPureColor()
{
	return pureColor;
}

void CC3MeshNode::setPureColor(cocos2d::ccColor4F newValue)
{
	pureColor = newValue;
}

bool CC3MeshNode::getShouldCullBackFaces()
{
	return shouldCullBackFaces;
}

void CC3MeshNode::setShouldCullBackFaces(bool newValue)
{
	shouldCullBackFaces = newValue;
}

bool CC3MeshNode::getShouldCullFrontFaces()
{
	return shouldCullFrontFaces;
}

void CC3MeshNode::setShouldCullFrontFaces(bool newValue)
{
	shouldCullFrontFaces = newValue;
}

void CC3MeshNode::alignTextures()
{
	mesh->alignWithTexturesIn(material);
	CC3LocalContentNode::alignTextures();
}

void CC3MeshNode::alignInvertedTextures()
{
	mesh->alignWithInvertedTexturesIn(material);
	CC3LocalContentNode::alignInvertedTextures();
}

cocos2d::CCRect CC3MeshNode::getTextureRectangle()
{
	return mesh ? mesh->getTextureRectangle() : cocos2d::CCRectZero;
}

void CC3MeshNode::setTextureRectangle(cocos2d::CCRect newValue)
{
	mesh->setTextureRectangle(newValue);
}

cocos2d::CCRect CC3MeshNode::textureRectangleForTextureUnit(GLuint texUnit)
{
	return mesh ? mesh->textureRectangleForTextureUnit(texUnit) : cocos2d::CCRectZero;//kCC3UnitTextureRectangle
}

void CC3MeshNode::setTextureRectangle(cocos2d::CCRect aRect, GLuint texUnit)
{
	mesh->setTextureRectangle(aRect, texUnit);
}

bool CC3MeshNode::getShouldUseLighting()
{
	return material ? material->getShouldUseLighting() : false;
}

void CC3MeshNode::setShouldUseLighting(bool useLighting)
{
	material->setShouldUseLighting(useLighting);
	CC3LocalContentNode::setShouldUseLighting(useLighting);	// pass along to any children
}

cocos2d::ccColor4F CC3MeshNode::getAmbientColor()
{
	return material ? material->getAmbientColor() : kCCC4FBlackTransparent;
}

void CC3MeshNode::setAmbientColor(cocos2d::ccColor4F newValue)
{
	material->setAmbientColor(newValue);
	CC3LocalContentNode::setAmbientColor(newValue);	// pass along to any children
}

cocos2d::ccColor4F CC3MeshNode::getDiffuseColor()
{
	return material ? material->getDiffuseColor() : kCCC4FBlackTransparent;
}

void CC3MeshNode::setDiffuseColor(cocos2d::ccColor4F newValue)
{
	material->setDiffuseColor(newValue);
	CC3LocalContentNode::setDiffuseColor(newValue);	// pass along to any children
}

cocos2d::ccColor4F CC3MeshNode::getSpecularColor()
{
	return material ? material->getSpecularColor() : kCCC4FBlackTransparent;
}

void CC3MeshNode::setSpecularColor(cocos2d::ccColor4F newValue)
{
	material->setSpecularColor(newValue);
	CC3LocalContentNode::setSpecularColor(newValue);	// pass along to any children
}

cocos2d::ccColor4F CC3MeshNode::getEmissionColor()
{
	return material ? material->getEmissionColor() : kCCC4FBlackTransparent;
}

void CC3MeshNode::setEmissionColor(cocos2d::ccColor4F newValue)
{
	material->setEmissionColor(newValue);
	CC3LocalContentNode::setEmissionColor(newValue);	// pass along to any children
}

CC3Vector CC3MeshNode::getGlobalLightLocation()
{
	return (material && material->getHasBumpMap())
	? getTransformMatrix()->transformDirection(material->getLightDirection())
	: CC3LocalContentNode::getGlobalLightLocation();
}

void CC3MeshNode::setGlobalLightLocation(CC3Vector aLocation)
{
	if (material && material->getHasBumpMap()) 
	{
		material->setLightDirection(getTransformMatrixInverted()->transformDirection(aLocation));
	}
	
	CC3LocalContentNode::setGlobalLightLocation(aLocation);
}

cocos2d::ccColor3B CC3MeshNode::getColor()
{
	return material ? material->getColor() : cocos2d::ccc3(CCColorByteFromFloat(pureColor.r),
											CCColorByteFromFloat(pureColor.g),
											CCColorByteFromFloat(pureColor.b));
}

void CC3MeshNode::setColor(cocos2d::ccColor3B color)
{
	material->setColor(color);
	
	pureColor.r = CCColorFloatFromByte(color.r);
	pureColor.g = CCColorFloatFromByte(color.g);
	pureColor.b = CCColorFloatFromByte(color.b);
	
	CC3LocalContentNode::setColor(color);	// pass along to any children
}

GLubyte CC3MeshNode::getOpacity()
{
	return material ? material->getOpacity() : CCColorByteFromFloat(pureColor.a);
}

void CC3MeshNode::setOpacity(GLubyte newValue)
{
	material->setOpacity(newValue);
	pureColor.a = CCColorFloatFromByte(newValue);
	
	CC3LocalContentNode::setOpacity(newValue);	// pass along to any children
}

bool CC3MeshNode::getIsOpaque()
{
	return material ? material->getIsOpaque() : (pureColor.a == 1.0f);
}

void CC3MeshNode::setIsOpaque(bool newValue)
{
	material->setIsOpaque(newValue);
	pureColor.a = 1.0f;
	
	CC3LocalContentNode::setIsOpaque(newValue);	// pass along to any children
}

void CC3MeshNode::drawLocalContentWithVisitor(CC3NodeDrawingVisitor* visitor)
{
	CC3OpenGLES11StateTrackerCapability* gles11Lighting = CC3OpenGLES11Engine::engine()->getServerCapabilities()->getLighting();
	
	// Remember current lighting state in case we disable it to apply pure color.
	bool lightingWasEnabled = gles11Lighting->getValue();
	
	configureDrawingParameters();		// Before material draws.
	
	drawMaterialWithVisitor(visitor);
	
	drawMeshWithVisitor(visitor);
	
	// Re-establish previous lighting state.
	gles11Lighting->setValue(lightingWasEnabled);
}

void CC3MeshNode::configureDrawingParameters()
{
	configureFaceCulling();
	configureNormalization();
	configureColoring();
}

void CC3MeshNode::configureFaceCulling()
{
	CC3OpenGLES11Engine* gles11Engine = CC3OpenGLES11Engine::engine();
	CC3OpenGLES11ServerCapabilities* gles11ServCaps = gles11Engine->getServerCapabilities();
	CC3OpenGLES11State* gles11State = gles11Engine->getState();
	
	// Enable culling if either back or front should be culled.
	gles11ServCaps->getCullFace()->setValue(shouldCullBackFaces || shouldCullFrontFaces);
	
	// Set whether back, front or both should be culled.
	// If neither should be culled, handled by capability so leave it as back culling.
	gles11State->getCullFace()->setValue(shouldCullBackFaces
	? (shouldCullFrontFaces ? GL_FRONT_AND_BACK : GL_BACK)
	: (shouldCullFrontFaces ? GL_FRONT : GL_BACK));
}

void CC3MeshNode::configureNormalization()
{
	CC3OpenGLES11ServerCapabilities* gles11ServCaps = CC3OpenGLES11Engine::engine()->getServerCapabilities();
	if (mesh && mesh->getHasNormals()) 
	{
		if (getIsUniformlyScaledGlobally()) 
		{
			gles11ServCaps->getRescaleNormal()->enable();
			gles11ServCaps->getNormalize()->disable();
		} 
		else 
		{
			gles11ServCaps->getRescaleNormal()->disable();
			gles11ServCaps->getNormalize()->enable();
		}
	} 
	else 
	{
		gles11ServCaps->getRescaleNormal()->disable();
		gles11ServCaps->getNormalize()->disable();
	}
}

void CC3MeshNode::configureColoring()
{
	CC3OpenGLES11Engine::engine()->getServerCapabilities()->getColorMaterial()->setValue((mesh ? mesh->getHasColors() : false));
}

void CC3MeshNode::drawMaterialWithVisitor(CC3NodeDrawingVisitor* visitor)
{
	CC3OpenGLES11Engine* gles11Engine = CC3OpenGLES11Engine::engine();
	CC3OpenGLES11StateTrackerCapability* gles11Lighting = gles11Engine->getServerCapabilities()->getLighting();
	if (visitor->getShouldDecorateNode()) 
	{
		if (material) 
		{
			material->drawWithVisitor(visitor);
		} 
		else 
		{
			CC3Material::unbind();
			gles11Lighting->disable();
			gles11Engine->getState()->getColor()->setValue(pureColor);
		}
	} 
	else 
	{
		CC3Material::unbind();
	}
}

void CC3MeshNode::drawMeshWithVisitor(CC3NodeDrawingVisitor* visitor)
{
	mesh->drawWithVisitor(visitor);
}

void CC3MeshNode::populateAsCenteredRectangleWithSize(cocos2d::CCSize rectSize)
{
	populateAsRectangleWithSizeAndPivot(rectSize, ccp(rectSize.width / 2.0, rectSize.height / 2.0));
}

void CC3MeshNode::populateAsCenteredRectangleWithSizeAndTessellation(cocos2d::CCSize rectSize, cocos2d::ccGridSize facesPerSide)
{
	populateAsRectangleWithSizeAndPivotAndTessellation(rectSize, ccp(rectSize.width / 2.0, rectSize.height / 2.0), facesPerSide);
}

void CC3MeshNode::populateAsCenteredRectangleWithSizeWithTextureAndInvertTexture(cocos2d::CCSize rectSize, CC3Texture* texture,
																				 bool shouldInvert)
{
	populateAsRectangleWithSizeAndPivotWithTextureAndInvertTexture(rectSize, 
																   ccp(rectSize.width / 2.0, rectSize.height / 2.0), texture, shouldInvert);
}

void CC3MeshNode::populateAsCenteredRectangleWithSizeAndTessellationWithTextureAndInvertTexture(cocos2d::CCSize rectSize, 
																								cocos2d::ccGridSize facesPerSide,
																								CC3Texture* texture,
																								bool shouldInvert)
{
	populateAsRectangleWithSizeAndPivotAndTessellationWithTextureAndInvertTexture(rectSize, 
																				  ccp(rectSize.width / 2.0, rectSize.height / 2.0),
																				  facesPerSide,
																				  texture,
																				  shouldInvert);
}

void CC3MeshNode::populateAsRectangleWithSizeAndPivot(cocos2d::CCSize rectSize, cocos2d::CCPoint pivot)
{
	populateAsRectangleWithSizeAndPivotAndTessellation(rectSize, pivot, cocos2d::ccg(1, 1));
}

void CC3MeshNode::populateAsRectangleWithSizeAndPivotAndTessellation(cocos2d::CCSize rectSize, cocos2d::CCPoint pivot, 
														cocos2d::ccGridSize facesPerSide)
{
	char itemName[256];
	CC3TexturedVertex* vertices;		// Array of custom structures to hold the interleaved vertex data
	
	// Must be at least one tessellation face per side of the rectangle.
	facesPerSide.x = MAX(facesPerSide.x, 1);
	facesPerSide.y = MAX(facesPerSide.y, 1);
	
	// Move the origin of the rectangle to the pivot point
	cocos2d::CCPoint botLeft = ccpSub(cocos2d::CCPointZero, pivot);
	cocos2d::CCPoint topRight = ccpSub(ccpFromSize(rectSize), pivot);
	
	// The size of each face in the tessellated grid
	cocos2d::CCSize faceSize = cocos2d::CCSizeMake((topRight.x - botLeft.x) / facesPerSide.x,
								 (topRight.y - botLeft.y) / facesPerSide.y);
	
	// Get vertices per side.
	cocos2d::ccGridSize verticesPerSide;
	verticesPerSide.x = facesPerSide.x + 1;
	verticesPerSide.y = facesPerSide.y + 1;
	int vertexCount = verticesPerSide.x * verticesPerSide.y;
	
	// Interleave the vertex locations, normals and tex coords
	// Create vertex location array, allocating enough space for the stride of the full structure
	sprintf(itemName, "%s-Locations", getName().c_str());
	CC3VertexLocations* locArray = CC3VertexLocations::vertexArrayWithName(itemName);
	locArray->setElementStride(sizeof(CC3TexturedVertex));	// Set stride before allocating elements.
	locArray->setElementOffset(0);							// Offset to location element in vertex structure
	vertices = (CC3TexturedVertex*)(locArray->allocateElements(vertexCount));
	
	// Create the normal array interleaved on the same element array
	sprintf(itemName, "%s-Normals", getName().c_str());
	CC3VertexNormals* normArray = CC3VertexNormals::vertexArrayWithName(itemName);
	normArray->setElements(vertices);
	normArray->setElementStride(locArray->getElementStride());	// Interleaved...so same stride
	normArray->setElementCount(vertexCount);
	normArray->setElementOffset(sizeof(CC3Vector));		// Offset to normal element in vertex structure
	
	// Populate vertex locations and normals in the X-Y plane
	// Iterate through the rows and columns of the vertex grid, from the bottom left corner,
	// and set the location of each vertex to be proportional to its position in the grid,
	// and set the normal of each vertex to point up the Z-axis.
	for (int iy = 0; iy < verticesPerSide.y; iy++) 
	{
		for (int ix = 0; ix < verticesPerSide.x; ix++) 
		{
			int vIndx = iy * verticesPerSide.x + ix;
			GLfloat vx = botLeft.x + (faceSize.width * ix);
			GLfloat vy = botLeft.y + (faceSize.height * iy);
			vertices[vIndx].location = cc3v(vx, vy, 0.0);
			vertices[vIndx].normal = kCC3VectorUnitZPositive;
		}
	}
	
	// Construct the vertex indices that will draw the triangles that make up each
	// face of the box. Indices are ordered for each of the six faces starting in
	// the lower left corner and proceeding counter-clockwise.
	GLuint triangleCount = facesPerSide.x * facesPerSide.y * 2;
	GLuint indexCount = triangleCount * 3;
	sprintf(itemName, "%s-Indices", getName().c_str());
	CC3VertexIndices* indexArray = CC3VertexIndices::vertexArrayWithName(itemName);
	indexArray->setDrawingMode(GL_TRIANGLES);
	indexArray->setElementType(GL_UNSIGNED_SHORT);
	indexArray->setElementCount(indexCount);
	GLushort* indices = (GLushort*)(indexArray->allocateElements(indexCount));
	
	// Iterate through the rows and columns of the faces in the grid, from the bottom left corner,
	// and specify the indexes of the three vertices in each of the two triangles of each face.
	int iIndx = 0;
	for (int iy = 0; iy < facesPerSide.y; iy++) 
	{
		for (int ix = 0; ix < facesPerSide.x; ix++) 
		{
			GLushort botLeftOfFace;
			
			// First triangle of face wound counter-clockwise
			botLeftOfFace = iy * verticesPerSide.x + ix;
			indices[iIndx++] = botLeftOfFace;							// Bottom left
			indices[iIndx++] = botLeftOfFace + 1;						// Bot right
			indices[iIndx++] = botLeftOfFace + verticesPerSide.x + 1;	// Top right
			
			// Second triangle of face wound counter-clockwise
			indices[iIndx++] = botLeftOfFace + verticesPerSide.x + 1;	// Top right
			indices[iIndx++] = botLeftOfFace + verticesPerSide.x;		// Top left
			indices[iIndx++] = botLeftOfFace;							// Bottom left
		}
	}
	
	// Create mesh with interleaved vertex arrays
	sprintf(itemName, "%s-Mesh", getName().c_str());
	CC3VertexArrayMesh* aMesh = CC3VertexArrayMesh::meshWithName(itemName);
	aMesh->setInterleaveVertices(true);
	aMesh->setVertexLocations(locArray);
	aMesh->setVertexNormals(normArray);
	aMesh->setVertexIndices(indexArray);
	setMesh(aMesh);
}

void CC3MeshNode::populateAsRectangleWithSizeAndPivotWithTextureAndInvertTexture(cocos2d::CCSize rectSize, cocos2d::CCPoint pivot, 
																	CC3Texture* texture,
																	bool shouldInvert)
{
	populateAsRectangleWithSizeAndPivotAndTessellationWithTextureAndInvertTexture(rectSize, ccp(rectSize.width / 2.0, rectSize.height / 2.0),
																				  cocos2d::ccg(1, 1),
																				  texture,
																				  shouldInvert);
}

void CC3MeshNode::populateAsRectangleWithSizeAndPivotAndTessellationWithTextureAndInvertTexture(cocos2d::CCSize rectSize,
																								cocos2d::CCPoint pivot,
																								cocos2d::ccGridSize facesPerSide,
																								CC3Texture* texture,
																								bool shouldInvert)
{
	char itemName[256];
	
	// Must be at least one tessellation face per side of the rectangle.
	facesPerSide.x = MAX(facesPerSide.x, 1);
	facesPerSide.y = MAX(facesPerSide.y, 1);
	
	// The size of each face in the tessellated grid
	cocos2d::CCSize faceSize = cocos2d::CCSizeMake((1.0 / facesPerSide.x), (1.0 / facesPerSide.y));
	
	// Get vertices per side.
	cocos2d::ccGridSize verticesPerSide;
	verticesPerSide.x = facesPerSide.x + 1;
	verticesPerSide.y = facesPerSide.y + 1;
	
	// Start as a basic white rectangle of the right size and location.
	populateAsRectangleWithSizeAndPivotAndTessellation(rectSize, pivot, facesPerSide);
	
	// Get my aMesh model and vertices.
	CC3VertexArrayMesh* vam = (CC3VertexArrayMesh*)getMesh(); 
	CC3VertexLocations* locArray = vam->getVertexLocations();
	
	// Create the tex coord array interleaved on the same element array as the vertex locations
	CC3VertexTextureCoordinates* tcArray = NULL;
	sprintf(itemName, "%s-Texture", getName().c_str());
	tcArray = CC3VertexTextureCoordinates::vertexArrayWithName(itemName);
	tcArray->setElements(locArray->getElements());
	tcArray->setElementStride(locArray->getElementStride());	// Interleaved...so same stride
	tcArray->setElementCount(locArray->getElementCount());
	tcArray->setElementOffset(2 * sizeof(CC3Vector));	// Offset to texcoord element in vertex structure
	
	// Add the texture coordinates array to the mesh
	vam->setVertexTextureCoordinates(tcArray);
	
	// Populate the texture coordinate array mapping
	CC3TexturedVertex* vertices = (CC3TexturedVertex*)(locArray->getElements());
	
	// Iterate through the rows and columns of the vertex grid, from the bottom left corner,
	// and set the X & Y texture coordinate of each vertex to be proportional to its position
	// in the grid.
	for (int iy = 0; iy < verticesPerSide.y; iy++) 
	{
		for (int ix = 0; ix < verticesPerSide.x; ix++) 
		{
			int vIndx = iy * verticesPerSide.x + ix;
			GLfloat vx = faceSize.width * ix;
			GLfloat vy = faceSize.height * iy;
			vertices[vIndx].texCoord = cocos2d::tex2(vx, vy);
		}
	}
	
	// Add a material and attach the texture
	sprintf(itemName, "%s-Material", getName().c_str());
	setMaterial(CC3Material::materialWithName(itemName));
	getMaterial()->setTexture(texture);
	
	// Align the texture coordinates to the texture
	if (shouldInvert) 
	{
		alignInvertedTextures();
	} 
	else 
	{
		alignTextures();
	}
}
																								
// Index data for the triangles covering the six faces of a solid box.
static const GLubyte solidBoxIndexData[] = {
1, 5, 7, 7, 3, 1,
0, 1, 3, 3, 2, 0,
4, 0, 2, 2, 6, 4,
5, 4, 6, 6, 7, 5,
3, 7, 6, 6, 2, 3,
0, 4, 5, 5, 1, 0,
};
																								
void CC3MeshNode::populateAsSolidBox(CC3BoundingBox box)
{
	char itemName[256];
	CC3TexturedVertex* vertices;		// Array of custom structures to hold the interleaved vertex data
	CC3Vector boxMin = box.minimum;
	CC3Vector boxMax = box.maximum;
	GLuint vertexCount = 8;
	
	// Create vertexLocation array.
	sprintf(itemName, "%s-Locations", getName().c_str());
	CC3VertexLocations* locArray = CC3VertexLocations::vertexArrayWithName(itemName);
	locArray->setElementStride(sizeof(CC3TexturedVertex));	// Set stride before allocating elements.
	locArray->setElementOffset(0);							// Offset to location element in vertex structure
	vertices = (CC3TexturedVertex*)(locArray->allocateElements(vertexCount));
	
	// Extract all 8 corner vertices from the box.
	vertices[0].location = cc3v(boxMin.x, boxMin.y, boxMin.z);
	vertices[1].location = cc3v(boxMin.x, boxMin.y, boxMax.z);
	vertices[2].location = cc3v(boxMin.x, boxMax.y, boxMin.z);
	vertices[3].location = cc3v(boxMin.x, boxMax.y, boxMax.z);
	vertices[4].location = cc3v(boxMax.x, boxMin.y, boxMin.z);
	vertices[5].location = cc3v(boxMax.x, boxMin.y, boxMax.z);
	vertices[6].location = cc3v(boxMax.x, boxMax.y, boxMin.z);
	vertices[7].location = cc3v(boxMax.x, boxMax.y, boxMax.z);
	
	// Create the normal array interleaved on the same element array
	sprintf(itemName, "%s-Normals", getName().c_str());
	CC3VertexNormals* normArray = CC3VertexNormals::vertexArrayWithName(itemName);
	normArray->setElements(vertices);
	normArray->setElementStride(locArray->getElementStride());	// Interleaved...so same stride
	normArray->setElementCount(vertexCount);
	normArray->setElementOffset(sizeof(CC3Vector));		// Offset to normal element in vertex structure
	
	// Since this is a box, and all sides meet at right angles, all components
	// of all normals will have a value of either positive or negative (1 / sqrt(3)).
	GLfloat oort = 1.0f / M_SQRT3;		// One-over-root-three
	
	// Populate normals up the positive Z-axis
	vertices[0].normal = cc3v(-oort, -oort, -oort);
	vertices[1].normal = cc3v(-oort, -oort,  oort);
	vertices[2].normal = cc3v(-oort,  oort, -oort);
	vertices[3].normal = cc3v(-oort,  oort,  oort);
	vertices[4].normal = cc3v( oort, -oort, -oort);
	vertices[5].normal = cc3v( oort, -oort,  oort);
	vertices[6].normal = cc3v( oort,  oort, -oort);
	vertices[7].normal = cc3v( oort,  oort,  oort);
	
	// Construct the vertex indices that will draw the triangles that make up each
	// face of the box. Indices are ordered for each of the six faces starting in
	// the lower left corner and proceeding counter-clockwise.
	GLuint triangleCount = 12;
	GLuint indexCount = triangleCount * 3;
	sprintf(itemName, "%s-Indices", getName().c_str());
	CC3VertexIndices* indexArray = CC3VertexIndices::vertexArrayWithName(itemName);
	indexArray->setDrawingMode(GL_TRIANGLES);
	indexArray->setElementType(GL_UNSIGNED_BYTE);
	indexArray->setElementCount(indexCount);
	indexArray->setElements((GLvoid*)solidBoxIndexData);
	
	// Create mesh with interleaved vertex arrays
	sprintf(itemName, "%s-Mesh", getName().c_str());
	CC3VertexArrayMesh* aMesh = CC3VertexArrayMesh::meshWithName(itemName);
	aMesh->setInterleaveVertices(true);
	aMesh->setVertexLocations(locArray);
	aMesh->setVertexNormals(normArray);
	aMesh->setVertexIndices(indexArray);
	setMesh(aMesh);
}
																								
// Vertex index data for the 12 lines of a wire box.
static const GLubyte wireBoxIndexData[] = {
	0, 1, 1, 3, 3, 2, 2, 0,
	4, 5, 5, 7, 7, 6, 6, 4,
	0, 4, 1, 5, 2, 6, 3, 7,
};
																								
void CC3MeshNode::populateAsWireBox(CC3BoundingBox box)
{
	char itemName[256];
	CC3Vector boxMin = box.minimum;
	CC3Vector boxMax = box.maximum;
	GLuint vertexCount = 8;
	
	// Create vertexLocation array.
	sprintf(itemName, "%s-Locations", getName().c_str());
	CC3VertexLocations* locArray = CC3VertexLocations::vertexArrayWithName(itemName);
	CC3Vector* vertices = (CC3Vector*)(locArray->allocateElements(vertexCount));
	
	// Extract all 8 corner vertices from the box.
	vertices[0] = cc3v(boxMin.x, boxMin.y, boxMin.z);
	vertices[1] = cc3v(boxMin.x, boxMin.y, boxMax.z);
	vertices[2] = cc3v(boxMin.x, boxMax.y, boxMin.z);
	vertices[3] = cc3v(boxMin.x, boxMax.y, boxMax.z);
	vertices[4] = cc3v(boxMax.x, boxMin.y, boxMin.z);
	vertices[5] = cc3v(boxMax.x, boxMin.y, boxMax.z);
	vertices[6] = cc3v(boxMax.x, boxMax.y, boxMin.z);
	vertices[7] = cc3v(boxMax.x, boxMax.y, boxMax.z);
	
	GLuint lineCount = 12;
	GLuint indexCount = lineCount * 2;
	sprintf(itemName, "%s-Indices", getName().c_str());
	CC3VertexIndices* indexArray = CC3VertexIndices::vertexArrayWithName(itemName);
	indexArray->setDrawingMode(GL_LINES);
	indexArray->setElementType(GL_UNSIGNED_BYTE);
	indexArray->setElementCount(indexCount);
	indexArray->setElements((GLvoid*)wireBoxIndexData);
	
	sprintf(itemName, "%s-Mesh", getName().c_str());
	CC3VertexArrayMesh* aMesh = CC3VertexArrayMesh::meshWithName(itemName);
	aMesh->setVertexLocations(locArray);
	aMesh->setVertexIndices(indexArray);
	setMesh(aMesh);
}
																								
void CC3MeshNode::populateAsLineStripWith(GLshort vertexCount, CC3Vector* vertices, bool shouldRetainVertices)
{
	char itemName[256];
	
	// Create vertexLocation array.
	sprintf(itemName, "%s-Locations", getName().c_str());
	CC3VertexLocations* locArray = CC3VertexLocations::vertexArrayWithName(itemName);
	locArray->setDrawingMode(GL_LINE_STRIP);
	if (shouldRetainVertices) 
	{
		locArray->allocateElements(vertexCount);
		memcpy(locArray->getElements(), vertices, vertexCount * sizeof(CC3Vector));
	} 
	else 
	{
		locArray->setElementCount(vertexCount);
		locArray->setElements(vertices);
	}
	
	sprintf(itemName, "%s-Mesh", getName().c_str());
	CC3VertexArrayMesh* aMesh = CC3VertexArrayMesh::meshWithName(itemName);
	aMesh->setVertexLocations(locArray);
	setMesh(aMesh);
}
																								
CC3Vector CC3MeshNode::vertexLocationAt(GLsizei index)
{
	return mesh ? mesh->vertexLocationAt(index) : kCC3VectorZero;
}
																								
void CC3MeshNode::setVertexLocation(CC3Vector aLocation, GLsizei index)
{
	mesh->setVertexLocation(aLocation, index);
}
																								
CC3Vector CC3MeshNode::vertexNormalAt(GLsizei index)
{
	return mesh ? mesh->vertexNormalAt(index) : kCC3VectorZero;
}
																								
void CC3MeshNode::setVertexNormal(CC3Vector aNormal, GLsizei index)
{
	mesh->setVertexNormal(aNormal, index);
}
																								
cocos2d::ccColor4F CC3MeshNode::vertexColor4FAt(GLsizei index)
{
	return mesh ? mesh->vertexColor4FAt(index) : kCCC4FBlackTransparent;
}
																								
void CC3MeshNode::setVertexColor4F(cocos2d::ccColor4F aColor, GLsizei index)
{
	mesh->setVertexColor4F(aColor, index);
}
																								
cocos2d::ccColor4B CC3MeshNode::vertexColor4BAt(GLsizei index)
{
	return mesh ? mesh->vertexColor4BAt(index) : cocos2d::ccc4(0, 0, 0, 0);
}
																								
void CC3MeshNode::setVertexColor4B(cocos2d::ccColor4B aColor, GLsizei index)
{
	mesh->setVertexColor4B(aColor, index);
}
																								
cocos2d::ccTex2F CC3MeshNode::vertexTexCoord2FAt(GLsizei index, GLuint texUnit)
{
	return mesh ? mesh->vertexTexCoord2FAt(index, texUnit) : cocos2d::tex2(0.0, 0.0);
}
																								
void CC3MeshNode::setVertexTexCoord2F(cocos2d::ccTex2F aTex2F, GLsizei index, GLuint texUnit)
{
	mesh->setVertexTexCoord2F(aTex2F, index, texUnit);
}
																								
cocos2d::ccTex2F CC3MeshNode::vertexTexCoord2FAt(GLsizei index)
{
	return vertexTexCoord2FAt(index, 0);
}
																								
void CC3MeshNode::setVertexTexCoord2F(cocos2d::ccTex2F aTex2F, GLsizei index)
{
	setVertexTexCoord2F(aTex2F, index, 0);
}
																								
GLushort CC3MeshNode::vertexIndexAt(GLsizei index)
{
	return mesh ? mesh->vertexIndexAt(index) : 0;
}
																								
void CC3MeshNode::setVertexIndex(GLushort vertexIndex, GLsizei index)
{
	mesh->setVertexIndex(vertexIndex, index);
}

//@interface CC3Node (TemplateMethods)
//-(void) populateFrom: (CC3Node*) another;
//-(void) updateBoundingVolume;
//-(void) transformMatrixChanged;
//@end
//
//@interface CC3MeshNode (TemplateMethods)
//-(void) configureDrawingParameters;
//-(void) configureFaceCulling;
//-(void) configureNormalization;
//-(void) configureColoring;
//-(void) drawMaterialWithVisitor: (CC3NodeDrawingVisitor*) visitor;
//-(void) drawMeshWithVisitor: (CC3NodeDrawingVisitor*) visitor;
//-(void) populateAsVertexBox: (CC3BoundingBox) box;
//@end
//
//@implementation CC3MeshNode
//
//@synthesize mesh, material, pureColor, shouldCullBackFaces, shouldCullFrontFaces;
//
//-(void) dealloc {
//	[mesh release];
//	[material release];
//	[super dealloc];
//}
//
//// Sets the mesh then, if a bounding volume exists, forces it to rebuild
//// using the new mesh data, or creates a default bounding volume from the mesh.
//-(void) setMesh:(CC3Mesh *) aMesh {
//	id oldMesh = mesh;
//	mesh = [aMesh retain];
//	[oldMesh release];
//	if (boundingVolume) {
//		[self.boundingVolume buildVolume];
//	} else {
//		self.boundingVolume = [mesh defaultBoundingVolume];
//	}
//
//}
//
//// Support for legacy CC3MeshModel class
//-(CC3MeshModel*) meshModel {
//	return (CC3MeshModel*)self.mesh;
//}
//
//// Support for legacy CC3MeshModel class
//-(void) setMeshModel: (CC3MeshModel *) aMesh {
//	self.mesh = aMesh;
//}
//
//// After setting the bounding volume, forces it to build its volume from the mesh
//-(void) setBoundingVolume:(CC3NodeBoundingVolume *) aBoundingVolume {
//	[super setBoundingVolume: aBoundingVolume];
//	[self.boundingVolume buildVolume];
//}
//
//-(void) alignTextures {
//	[mesh alignWithTexturesIn: material];
//	[super alignTextures];
//}
//
//-(void) alignInvertedTextures {
//	[mesh alignWithInvertedTexturesIn: material];
//	[super alignInvertedTextures];
//}
//
//-(CGRect) textureRectangle {
//	return mesh ? mesh.textureRectangle : kCC3UnitTextureRectangle;
//}
//
//-(void) setTextureRectangle: (CGRect) aRect {
//	mesh.textureRectangle = aRect;
//}
//
//-(CGRect) textureRectangleForTextureUnit: (GLuint) texUnit {
//	return mesh ? [mesh textureRectangleForTextureUnit: texUnit] : kCC3UnitTextureRectangle;
//}
//
//-(void) setTextureRectangle: (CGRect) aRect forTextureUnit: (GLuint) texUnit {
//	[mesh setTextureRectangle: aRect forTextureUnit: texUnit];
//}
//
//-(CC3BoundingBox) localContentBoundingBox {
//	return mesh ? mesh.boundingBox : kCC3BoundingBoxNull;
//}
//
//#pragma mark Material coloring
//
//-(BOOL) shouldUseLighting {
//	return material ? material.shouldUseLighting : NO;
//}
//
//-(void) setShouldUseLighting: (BOOL) useLighting {
//	material.shouldUseLighting = useLighting;
//	[super setShouldUseLighting: useLighting];	// pass along to any children
//}
//
//-(ccColor4F) ambientColor {
//	return material ? material.ambientColor : kCCC4FBlackTransparent;
//}
//
//-(void) setAmbientColor:(ccColor4F) aColor {
//	material.ambientColor = aColor;
//	[super setAmbientColor: aColor];	// pass along to any children
//}
//
//-(ccColor4F) diffuseColor {
//	return material ? material.diffuseColor : kCCC4FBlackTransparent;
//}
//
//-(void) setDiffuseColor:(ccColor4F) aColor {
//	material.diffuseColor = aColor;
//	[super setDiffuseColor: aColor];	// pass along to any children
//}
//
//-(ccColor4F) specularColor {
//	return material ? material.specularColor : kCCC4FBlackTransparent;
//}
//
//-(void) setSpecularColor:(ccColor4F) aColor {
//	material.specularColor = aColor;
//	[super setSpecularColor: aColor];	// pass along to any children
//}
//
//-(ccColor4F) emissionColor {
//	return material ? material.emissionColor : kCCC4FBlackTransparent;
//}
//
//-(void) setEmissionColor:(ccColor4F) aColor {
//	material.emissionColor = aColor;
//	[super setEmissionColor: aColor];	// pass along to any children
//}
//
///** If the material has a bump-mapped texture, returns the global direction  */
//-(CC3Vector) globalLightLocation {
//	return (material && material.hasBumpMap)
//			? [self.transformMatrix transformDirection: material.lightDirection]
//			: [super globalLightLocation];
//}
//
//-(void) setGlobalLightLocation: (CC3Vector) aLocation {
//	if (material && material.hasBumpMap) {
//		material.lightDirection = [self.transformMatrixInverted transformDirection: aLocation];
//	}
//	[super setGlobalLightLocation: aLocation];
//}
//
//
//#pragma mark CCRGBAProtocol support
//
//-(ccColor3B) color {
//	return material ? material.color : ccc3(CCColorByteFromFloat(pureColor.r),
//											CCColorByteFromFloat(pureColor.g),
//											CCColorByteFromFloat(pureColor.b));
//}
//
//-(void) setColor: (ccColor3B) color {
//	material.color = color;
//
//	pureColor.r = CCColorFloatFromByte(color.r);
//	pureColor.g = CCColorFloatFromByte(color.g);
//	pureColor.b = CCColorFloatFromByte(color.b);
//
//	[super setColor: color];	// pass along to any children
//}
//
//-(GLubyte) opacity {
//	return material ? material.opacity : CCColorByteFromFloat(pureColor.a);
//}
//
//-(void) setOpacity: (GLubyte) opacity {
//	material.opacity = opacity;
//	pureColor.a = CCColorFloatFromByte(opacity);
//
//	[super setOpacity: opacity];	// pass along to any children
//}
//
//-(BOOL) isOpaque {
//	return material ? material.isOpaque : (pureColor.a == 1.0f);
//}
//
//-(void) setIsOpaque: (BOOL) opaque {
//	material.isOpaque = opaque;
//	pureColor.a = 1.0f;
//	
//	[super setIsOpaque: opaque];	// pass along to any children
//}
//
//
//#pragma mark Allocation and initialization
//
//-(id) initWithTag: (GLuint) aTag withName: (NSString*) aName {
//	if ( (self = [super initWithTag: aTag withName: aName]) ) {
//		pureColor = kCCC4FWhite;
//		shouldCullBackFaces = YES;
//		shouldCullFrontFaces = NO;
//	}
//	return self;
//}
//
//-(void) createGLBuffers {
//	LogTrace(@"%@ creating GL server buffers", self);
//	[mesh createGLBuffers];
//	[super createGLBuffers];
//}
//
//-(void) deleteGLBuffers {
//	[mesh deleteGLBuffers];
//	[super deleteGLBuffers];
//}
//
//-(void) releaseRedundantData {
//	[mesh releaseRedundantData];
//	[super releaseRedundantData];
//}
//
//-(void) retainVertexLocations {
//	[mesh retainVertexLocations];
//	[super retainVertexLocations];
//}
//
//-(void) retainVertexNormals {
//	[mesh retainVertexNormals];
//	[super retainVertexNormals];
//}
//
//-(void) retainVertexColors {
//	[mesh retainVertexColors];
//	[super retainVertexColors];
//}
//
//-(void) retainVertexTextureCoordinates {
//	[mesh retainVertexTextureCoordinates];
//	[super retainVertexTextureCoordinates];
//}
//
//-(void) retainVertexIndices {
//	[mesh retainVertexIndices];
//	[super retainVertexIndices];
//}
//
//-(void) doNotBufferVertexLocations {
//	[mesh doNotBufferVertexLocations];
//	[super doNotBufferVertexLocations];
//}
//
//-(void) doNotBufferVertexNormals {
//	[mesh doNotBufferVertexNormals];
//	[super doNotBufferVertexNormals];
//}
//
//-(void) doNotBufferVertexColors {
//	[mesh doNotBufferVertexColors];
//	[super doNotBufferVertexColors];
//}
//
//-(void) doNotBufferVertexTextureCoordinates {
//	[mesh doNotBufferVertexTextureCoordinates];
//	[super doNotBufferVertexTextureCoordinates];
//}
//
//-(void) doNotBufferVertexIndices {
//	[mesh doNotBufferVertexIndices];
//	[super doNotBufferVertexIndices];
//}
//
//// Template method that populates this instance from the specified other instance.
//// This method is invoked automatically during object copying via the copyWithZone: method.
//// A copy is made of the material.
//// The mesh is simply retained, without creating a copy.
//// Both this node and the other node will share the mesh.
//-(void) populateFrom: (CC3MeshNode*) another {
//	[super populateFrom: another];
//	
//	self.mesh = another.mesh;						// retained
//	self.material = [another.material copyAutoreleased];	// retained
//
//	pureColor = another.pureColor;
//	shouldCullBackFaces = another.shouldCullBackFaces;
//	shouldCullFrontFaces = another.shouldCullFrontFaces;
//}
//
//-(void) populateAsCenteredRectangleWithSize: (CGSize) rectSize {
//	[self populateAsRectangleWithSize: rectSize
//							 andPivot: ccp(rectSize.width / 2.0, rectSize.height / 2.0)];
//}
//
//-(void) populateAsCenteredRectangleWithSize: (CGSize) rectSize
//							andTessellation: (ccGridSize) facesPerSide {
//	[self populateAsRectangleWithSize: rectSize
//							 andPivot: ccp(rectSize.width / 2.0, rectSize.height / 2.0)
//					  andTessellation: facesPerSide];
//}
//
//-(void) populateAsRectangleWithSize: (CGSize) rectSize andPivot: (CGPoint) pivot {
//	[self populateAsRectangleWithSize: rectSize andPivot: pivot andTessellation: ccg(1, 1)];
//}
//
//-(void) populateAsRectangleWithSize: (CGSize) rectSize
//						   andPivot: (CGPoint) pivot
//					andTessellation: (ccGridSize) facesPerSide {
//	NSString* itemName;
//	CC3TexturedVertex* vertices;		// Array of custom structures to hold the interleaved vertex data
//	
//	// Must be at least one tessellation face per side of the rectangle.
//	facesPerSide.x = MAX(facesPerSide.x, 1);
//	facesPerSide.y = MAX(facesPerSide.y, 1);
//
//	// Move the origin of the rectangle to the pivot point
//	CGPoint botLeft = ccpSub(CGPointZero, pivot);
//	CGPoint topRight = ccpSub(ccpFromSize(rectSize), pivot);
//
//	// The size of each face in the tessellated grid
//	CGSize faceSize = CGSizeMake((topRight.x - botLeft.x) / facesPerSide.x,
//								 (topRight.y - botLeft.y) / facesPerSide.y);
//
//	// Get vertices per side.
//	ccGridSize verticesPerSide;
//	verticesPerSide.x = facesPerSide.x + 1;
//	verticesPerSide.y = facesPerSide.y + 1;
//	int vertexCount = verticesPerSide.x * verticesPerSide.y;
//	
//	// Interleave the vertex locations, normals and tex coords
//	// Create vertex location array, allocating enough space for the stride of the full structure
//	itemName = [NSString stringWithFormat: @"%@-Locations", self.name];
//	CC3VertexLocations* locArray = [CC3VertexLocations vertexArrayWithName: itemName];
//	locArray.elementStride = sizeof(CC3TexturedVertex);	// Set stride before allocating elements.
//	locArray.elementOffset = 0;							// Offset to location element in vertex structure
//	vertices = [locArray allocateElements: vertexCount];
//	
//	// Create the normal array interleaved on the same element array
//	itemName = [NSString stringWithFormat: @"%@-Normals", self.name];
//	CC3VertexNormals* normArray = [CC3VertexNormals vertexArrayWithName: itemName];
//	normArray.elements = vertices;
//	normArray.elementStride = locArray.elementStride;	// Interleaved...so same stride
//	normArray.elementCount = vertexCount;
//	normArray.elementOffset = sizeof(CC3Vector);		// Offset to normal element in vertex structure
//
//	// Populate vertex locations and normals in the X-Y plane
//	// Iterate through the rows and columns of the vertex grid, from the bottom left corner,
//	// and set the location of each vertex to be proportional to its position in the grid,
//	// and set the normal of each vertex to point up the Z-axis.
//	for (int iy = 0; iy < verticesPerSide.y; iy++) {
//		for (int ix = 0; ix < verticesPerSide.x; ix++) {
//			int vIndx = iy * verticesPerSide.x + ix;
//			GLfloat vx = botLeft.x + (faceSize.width * ix);
//			GLfloat vy = botLeft.y + (faceSize.height * iy);
//			vertices[vIndx].location = cc3v(vx, vy, 0.0);
//			vertices[vIndx].normal = kCC3VectorUnitZPositive;
//		}
//	}
//	
//	// Construct the vertex indices that will draw the triangles that make up each
//	// face of the box. Indices are ordered for each of the six faces starting in
//	// the lower left corner and proceeding counter-clockwise.
//	GLuint triangleCount = facesPerSide.x * facesPerSide.y * 2;
//	GLuint indexCount = triangleCount * 3;
//	itemName = [NSString stringWithFormat: @"%@-Indices", self.name];
//	CC3VertexIndices* indexArray = [CC3VertexIndices vertexArrayWithName: itemName];
//	indexArray.drawingMode = GL_TRIANGLES;
//	indexArray.elementType = GL_UNSIGNED_SHORT;
//	indexArray.elementCount = indexCount;
//	GLushort* indices = [indexArray allocateElements: indexCount];
//	
//	// Iterate through the rows and columns of the faces in the grid, from the bottom left corner,
//	// and specify the indexes of the three vertices in each of the two triangles of each face.
//	int iIndx = 0;
//	for (int iy = 0; iy < facesPerSide.y; iy++) {
//		for (int ix = 0; ix < facesPerSide.x; ix++) {
//			GLushort botLeftOfFace;
//			
//			// First triangle of face wound counter-clockwise
//			botLeftOfFace = iy * verticesPerSide.x + ix;
//			indices[iIndx++] = botLeftOfFace;							// Bottom left
//			indices[iIndx++] = botLeftOfFace + 1;						// Bot right
//			indices[iIndx++] = botLeftOfFace + verticesPerSide.x + 1;	// Top right
//
//			// Second triangle of face wound counter-clockwise
//			indices[iIndx++] = botLeftOfFace + verticesPerSide.x + 1;	// Top right
//			indices[iIndx++] = botLeftOfFace + verticesPerSide.x;		// Top left
//			indices[iIndx++] = botLeftOfFace;							// Bottom left
//		}
//	}
//	
//	// Create mesh with interleaved vertex arrays
//	itemName = [NSString stringWithFormat: @"%@-Mesh", self.name];
//	CC3VertexArrayMesh* aMesh = [CC3VertexArrayMesh meshWithName: itemName];
//	aMesh.interleaveVertices = YES;
//	aMesh.vertexLocations = locArray;
//	aMesh.vertexNormals = normArray;
//	aMesh.vertexIndices = indexArray;
//	self.mesh = aMesh;
//}
//
//-(void) populateAsCenteredRectangleWithSize: (CGSize) rectSize
//								withTexture: (CC3Texture*) texture
//							  invertTexture: (BOOL) shouldInvert {
//	[self populateAsRectangleWithSize: rectSize
//							 andPivot: ccp(rectSize.width / 2.0, rectSize.height / 2.0)
//						  withTexture: texture
//						invertTexture: shouldInvert];
//}
//
//-(void) populateAsCenteredRectangleWithSize: (CGSize) rectSize
//							andTessellation: (ccGridSize) facesPerSide
//								withTexture: (CC3Texture*) texture
//							  invertTexture: (BOOL) shouldInvert {
//	[self populateAsRectangleWithSize: rectSize
//							 andPivot: ccp(rectSize.width / 2.0, rectSize.height / 2.0)
//					  andTessellation: facesPerSide
//						  withTexture: texture
//						invertTexture: shouldInvert];
//}
//
//-(void) populateAsRectangleWithSize: (CGSize) rectSize
//						   andPivot: (CGPoint) pivot
//						withTexture: (CC3Texture*) texture
//					  invertTexture: (BOOL) shouldInvert {
//	[self populateAsRectangleWithSize: rectSize
//							 andPivot: ccp(rectSize.width / 2.0, rectSize.height / 2.0)
//					  andTessellation:  ccg(1, 1)
//						  withTexture: texture
//						invertTexture: shouldInvert];
//}
//
//-(void) populateAsRectangleWithSize: (CGSize) rectSize
//						   andPivot: (CGPoint) pivot
//					andTessellation: (ccGridSize) facesPerSide
//						withTexture: (CC3Texture*) texture
//					  invertTexture: (BOOL) shouldInvert {
//	NSString* itemName;
//	
//	// Must be at least one tessellation face per side of the rectangle.
//	facesPerSide.x = MAX(facesPerSide.x, 1);
//	facesPerSide.y = MAX(facesPerSide.y, 1);
//	
//	// The size of each face in the tessellated grid
//	CGSize faceSize = CGSizeMake((1.0 / facesPerSide.x), (1.0 / facesPerSide.y));
//	
//	// Get vertices per side.
//	ccGridSize verticesPerSide;
//	verticesPerSide.x = facesPerSide.x + 1;
//	verticesPerSide.y = facesPerSide.y + 1;
//
//	// Start as a basic white rectangle of the right size and location.
//	[self populateAsRectangleWithSize: rectSize andPivot: pivot andTessellation: facesPerSide];
//	
//	// Get my aMesh model and vertices.
//	CC3VertexArrayMesh* vam = (CC3VertexArrayMesh*)self.mesh; 
//	CC3VertexLocations* locArray = vam.vertexLocations;
//	
//	// Create the tex coord array interleaved on the same element array as the vertex locations
//	CC3VertexTextureCoordinates* tcArray = nil;
//	itemName = [NSString stringWithFormat: @"%@-Texture", self.name];
//	tcArray = [CC3VertexTextureCoordinates vertexArrayWithName: itemName];
//	tcArray.elements = locArray.elements;
//	tcArray.elementStride = locArray.elementStride;	// Interleaved...so same stride
//	tcArray.elementCount = locArray.elementCount;
//	tcArray.elementOffset = 2 * sizeof(CC3Vector);	// Offset to texcoord element in vertex structure
//
//	// Add the texture coordinates array to the mesh
//	vam.vertexTextureCoordinates = tcArray;
//	
//	// Populate the texture coordinate array mapping
//	CC3TexturedVertex* vertices = locArray.elements;
//	
//	// Iterate through the rows and columns of the vertex grid, from the bottom left corner,
//	// and set the X & Y texture coordinate of each vertex to be proportional to its position
//	// in the grid.
//	for (int iy = 0; iy < verticesPerSide.y; iy++) {
//		for (int ix = 0; ix < verticesPerSide.x; ix++) {
//			int vIndx = iy * verticesPerSide.x + ix;
//			GLfloat vx = faceSize.width * ix;
//			GLfloat vy = faceSize.height * iy;
//			vertices[vIndx].texCoord = (ccTex2F){vx, vy};
//		}
//	}
//	
//	// Add a material and attach the texture
//	itemName = [NSString stringWithFormat: @"%@-Material", self.name];
//	self.material = [CC3Material materialWithName: itemName];
//	self.material.texture = texture;
//	
//	// Align the texture coordinates to the texture
//	if (shouldInvert) {
//		[self alignInvertedTextures];
//	} else {
//		[self alignTextures];
//	}
//}
//
//// Index data for the triangles covering the six faces of a solid box.
//static const GLubyte solidBoxIndexData[] = {
//	1, 5, 7, 7, 3, 1,
//	0, 1, 3, 3, 2, 0,
//	4, 0, 2, 2, 6, 4,
//	5, 4, 6, 6, 7, 5,
//	3, 7, 6, 6, 2, 3,
//	0, 4, 5, 5, 1, 0,
//};
//
//-(void) populateAsSolidBox: (CC3BoundingBox) box {
//	NSString* itemName;
//	CC3TexturedVertex* vertices;		// Array of custom structures to hold the interleaved vertex data
//	CC3Vector boxMin = box.minimum;
//	CC3Vector boxMax = box.maximum;
//	GLuint vertexCount = 8;
//	
//	// Create vertexLocation array.
//	itemName = [NSString stringWithFormat: @"%@-Locations", self.name];
//	CC3VertexLocations* locArray = [CC3VertexLocations vertexArrayWithName: itemName];
//	locArray.elementStride = sizeof(CC3TexturedVertex);	// Set stride before allocating elements.
//	locArray.elementOffset = 0;							// Offset to location element in vertex structure
//	vertices = [locArray allocateElements: vertexCount];
//	
//	// Extract all 8 corner vertices from the box.
//	vertices[0].location = cc3v(boxMin.x, boxMin.y, boxMin.z);
//	vertices[1].location = cc3v(boxMin.x, boxMin.y, boxMax.z);
//	vertices[2].location = cc3v(boxMin.x, boxMax.y, boxMin.z);
//	vertices[3].location = cc3v(boxMin.x, boxMax.y, boxMax.z);
//	vertices[4].location = cc3v(boxMax.x, boxMin.y, boxMin.z);
//	vertices[5].location = cc3v(boxMax.x, boxMin.y, boxMax.z);
//	vertices[6].location = cc3v(boxMax.x, boxMax.y, boxMin.z);
//	vertices[7].location = cc3v(boxMax.x, boxMax.y, boxMax.z);
//	
//	// Create the normal array interleaved on the same element array
//	itemName = [NSString stringWithFormat: @"%@-Normals", self.name];
//	CC3VertexNormals* normArray = [CC3VertexNormals vertexArrayWithName: itemName];
//	normArray.elements = vertices;
//	normArray.elementStride = locArray.elementStride;	// Interleaved...so same stride
//	normArray.elementCount = vertexCount;
//	normArray.elementOffset = sizeof(CC3Vector);		// Offset to normal element in vertex structure
//
//	// Since this is a box, and all sides meet at right angles, all components
//	// of all normals will have a value of either positive or negative (1 / sqrt(3)).
//	GLfloat oort = 1.0f / M_SQRT3;		// One-over-root-three
//	
//	// Populate normals up the positive Z-axis
//	vertices[0].normal = cc3v(-oort, -oort, -oort);
//	vertices[1].normal = cc3v(-oort, -oort,  oort);
//	vertices[2].normal = cc3v(-oort,  oort, -oort);
//	vertices[3].normal = cc3v(-oort,  oort,  oort);
//	vertices[4].normal = cc3v( oort, -oort, -oort);
//	vertices[5].normal = cc3v( oort, -oort,  oort);
//	vertices[6].normal = cc3v( oort,  oort, -oort);
//	vertices[7].normal = cc3v( oort,  oort,  oort);
//	
//	// Construct the vertex indices that will draw the triangles that make up each
//	// face of the box. Indices are ordered for each of the six faces starting in
//	// the lower left corner and proceeding counter-clockwise.
//	GLuint triangleCount = 12;
//	GLuint indexCount = triangleCount * 3;
//	itemName = [NSString stringWithFormat: @"%@-Indices", self.name];
//	CC3VertexIndices* indexArray = [CC3VertexIndices vertexArrayWithName: itemName];
//	indexArray.drawingMode = GL_TRIANGLES;
//	indexArray.elementType = GL_UNSIGNED_BYTE;
//	indexArray.elementCount = indexCount;
//	indexArray.elements = (GLvoid*)solidBoxIndexData;
//	
//	// Create mesh with interleaved vertex arrays
//	itemName = [NSString stringWithFormat: @"%@-Mesh", self.name];
//	CC3VertexArrayMesh* aMesh = [CC3VertexArrayMesh meshWithName: itemName];
//	aMesh.interleaveVertices = YES;
//	aMesh.vertexLocations = locArray;
//	aMesh.vertexNormals = normArray;
//	aMesh.vertexIndices = indexArray;
//	self.mesh = aMesh;
//}
//
//// Vertex index data for the 12 lines of a wire box.
//static const GLubyte wireBoxIndexData[] = {
//	0, 1, 1, 3, 3, 2, 2, 0,
//	4, 5, 5, 7, 7, 6, 6, 4,
//	0, 4, 1, 5, 2, 6, 3, 7,
//};
//
//-(void) populateAsWireBox: (CC3BoundingBox) box {
//	NSString* itemName;
//	CC3Vector boxMin = box.minimum;
//	CC3Vector boxMax = box.maximum;
//	GLuint vertexCount = 8;
//	
//	// Create vertexLocation array.
//	itemName = [NSString stringWithFormat: @"%@-Locations", self.name];
//	CC3VertexLocations* locArray = [CC3VertexLocations vertexArrayWithName: itemName];
//	CC3Vector* vertices = [locArray allocateElements: vertexCount];
//	
//	// Extract all 8 corner vertices from the box.
//	vertices[0] = cc3v(boxMin.x, boxMin.y, boxMin.z);
//	vertices[1] = cc3v(boxMin.x, boxMin.y, boxMax.z);
//	vertices[2] = cc3v(boxMin.x, boxMax.y, boxMin.z);
//	vertices[3] = cc3v(boxMin.x, boxMax.y, boxMax.z);
//	vertices[4] = cc3v(boxMax.x, boxMin.y, boxMin.z);
//	vertices[5] = cc3v(boxMax.x, boxMin.y, boxMax.z);
//	vertices[6] = cc3v(boxMax.x, boxMax.y, boxMin.z);
//	vertices[7] = cc3v(boxMax.x, boxMax.y, boxMax.z);
//	
//	GLuint lineCount = 12;
//	GLuint indexCount = lineCount * 2;
//	itemName = [NSString stringWithFormat: @"%@-Indices", self.name];
//	CC3VertexIndices* indexArray = [CC3VertexIndices vertexArrayWithName: itemName];
//	indexArray.drawingMode = GL_LINES;
//	indexArray.elementType = GL_UNSIGNED_BYTE;
//	indexArray.elementCount = indexCount;
//	indexArray.elements = (GLvoid*)wireBoxIndexData;
//	
//	itemName = [NSString stringWithFormat: @"%@-Mesh", self.name];
//	CC3VertexArrayMesh* aMesh = [CC3VertexArrayMesh meshWithName: itemName];
//	aMesh.vertexLocations = locArray;
//	aMesh.vertexIndices = indexArray;
//	self.mesh = aMesh;
//}
//
//-(void) populateAsLineStripWith: (GLshort) vertexCount
//					   vertices: (CC3Vector*) vertices
//					  andRetain: (BOOL) shouldRetainVertices {
//	NSString* itemName;
//	
//	// Create vertexLocation array.
//	itemName = [NSString stringWithFormat: @"%@-Locations", self.name];
//	CC3VertexLocations* locArray = [CC3VertexLocations vertexArrayWithName: itemName];
//	locArray.drawingMode = GL_LINE_STRIP;
//	if (shouldRetainVertices) {
//		[locArray allocateElements: vertexCount];
//		memcpy(locArray.elements, vertices, vertexCount * sizeof(CC3Vector));
//	} else {
//		locArray.elementCount = vertexCount;
//		locArray.elements = vertices;
//	}
//	
//	itemName = [NSString stringWithFormat: @"%@-Mesh", self.name];
//	CC3VertexArrayMesh* aMesh = [CC3VertexArrayMesh meshWithName: itemName];
//	aMesh.vertexLocations = locArray;
//	self.mesh = aMesh;
//}
//
//
//#pragma mark Type testing
//
//-(BOOL) isMeshNode {
//	return YES;
//}
//
//#pragma mark Drawing
//
///**
// * If we have a material, delegates to the material to set material and texture state,
// * otherwise, establishes the pure color by turning lighting off and setting the color.
// * One material or color is set, delegates to the mesh to draw mesh.
// */
//-(void) drawLocalContentWithVisitor: (CC3NodeDrawingVisitor*) visitor {
//	CC3OpenGLES11StateTrackerCapability* gles11Lighting = [CC3OpenGLES11Engine engine].serverCapabilities.lighting;
//
//	// Remember current lighting state in case we disable it to apply pure color.
//	BOOL lightingWasEnabled = gles11Lighting.value;
//
//	[self configureDrawingParameters];		// Before material draws.
//
//	[self drawMaterialWithVisitor: visitor];
//
//	[self drawMeshWithVisitor: visitor];
//
//	// Re-establish previous lighting state.
//	gles11Lighting.value = lightingWasEnabled;
//}
//
///**
// * Configures the drawing parameters.
// *
// * The default implementation configures normalization and vertex coloring.
// * Subclasses may override to add additional drawing parameters.
// */
//-(void) configureDrawingParameters {
//	[self configureFaceCulling];
//	[self configureNormalization];
//	[self configureColoring];
//}
//
///** Configures GL face culling based on the shouldCullBackFaces and shouldCullBackFaces properties. */
//-(void) configureFaceCulling {
//	CC3OpenGLES11Engine* gles11Engine = [CC3OpenGLES11Engine engine];
//	CC3OpenGLES11ServerCapabilities* gles11ServCaps = gles11Engine.serverCapabilities;
//	CC3OpenGLES11State* gles11State = gles11Engine.state;
//
//	// Enable culling if either back or front should be culled.
//	gles11ServCaps.cullFace.value = shouldCullBackFaces || shouldCullFrontFaces;
//
//	// Set whether back, front or both should be culled.
//	// If neither should be culled, handled by capability so leave it as back culling.
//	gles11State.cullFace.value = shouldCullBackFaces
//									? (shouldCullFrontFaces ? GL_FRONT_AND_BACK : GL_BACK)
//									: (shouldCullFrontFaces ? GL_FRONT : GL_BACK);
//
//}
//
///** Configures GL scaling of normals, based on whether the scaling of this node is uniform or not. */
//-(void) configureNormalization {
//	CC3OpenGLES11ServerCapabilities* gles11ServCaps = [CC3OpenGLES11Engine engine].serverCapabilities;
//	if (mesh && mesh.hasNormals) {
//		if (self.isUniformlyScaledGlobally) {
//			[gles11ServCaps.rescaleNormal enable];
//			[gles11ServCaps.normalize disable];
//		} else {
//			[gles11ServCaps.rescaleNormal disable];
//			[gles11ServCaps.normalize enable];
//		}
//	} else {
//		[gles11ServCaps.rescaleNormal disable];
//		[gles11ServCaps.normalize disable];
//	}
//}
//
///**
// * Configures the GL state to support vertex coloring. This must be invoked every time, because
// * both the material and mesh influence this property, and the mesh will not be re-bound if it
// * does not need to be switched. And this method must be invoked before material colors are set,
// * otherwise material colors will not stick.
// */
//-(void) configureColoring {
//	[CC3OpenGLES11Engine engine].serverCapabilities.colorMaterial.value = (mesh ? mesh.hasColors : NO);
//}
//
///** Template method to draw the material to the GL engine. */
//-(void) drawMaterialWithVisitor: (CC3NodeDrawingVisitor*) visitor {
//	CC3OpenGLES11Engine* gles11Engine = [CC3OpenGLES11Engine engine];
//	CC3OpenGLES11StateTrackerCapability* gles11Lighting = gles11Engine.serverCapabilities.lighting;
//	if (visitor.shouldDecorateNode) {
//		if (material) {
//			[material drawWithVisitor: visitor];
//		} else {
//			[CC3Material unbind];
//			[gles11Lighting disable];
//			gles11Engine.state.color.value = pureColor;
//		}
//	} else {
//		[CC3Material unbind];
//	}
//}
//
///** Template method to draw the mesh to the GL engine. */
//-(void) drawMeshWithVisitor: (CC3NodeDrawingVisitor*) visitor {
//	[mesh drawWithVisitor: visitor];
//}
//
//
//#pragma mark Accessing vertex data
//
//-(CC3Vector) vertexLocationAt: (GLsizei) index {
//	return mesh ? [mesh vertexLocationAt: index] : kCC3VectorZero;
//}
//
//-(void) setVertexLocation: (CC3Vector) aLocation at: (GLsizei) index {
//	[mesh setVertexLocation: aLocation at: index];
//}
//
//-(CC3Vector) vertexNormalAt: (GLsizei) index {
//	return mesh ? [mesh vertexNormalAt: index] : kCC3VectorZero;
//}
//
//-(void) setVertexNormal: (CC3Vector) aNormal at: (GLsizei) index {
//	[mesh setVertexNormal: aNormal at: index];
//}
//
//-(ccColor4F) vertexColor4FAt: (GLsizei) index {
//	return mesh ? [mesh vertexColor4FAt: index] : kCCC4FBlackTransparent;
//}
//
//-(void) setVertexColor4F: (ccColor4F) aColor at: (GLsizei) index {
//	[mesh setVertexColor4F: aColor at: index];
//}
//
//-(ccColor4B) vertexColor4BAt: (GLsizei) index {
//	return mesh ? [mesh vertexColor4BAt: index] : (ccColor4B){ 0, 0, 0, 0 };
//}
//
//-(void) setVertexColor4B: (ccColor4B) aColor at: (GLsizei) index {
//	[mesh setVertexColor4B: aColor at: index];
//}
//
//-(ccTex2F) vertexTexCoord2FAt: (GLsizei) index forTextureUnit: (GLuint) texUnit {
//	return mesh ? [mesh vertexTexCoord2FAt: index forTextureUnit: texUnit] : (ccTex2F){ 0.0, 0.0 };
//}
//
//-(void) setVertexTexCoord2F: (ccTex2F) aTex2F at: (GLsizei) index forTextureUnit: (GLuint) texUnit {
//	[mesh setVertexTexCoord2F: aTex2F at: index forTextureUnit: texUnit];
//}
//
//-(ccTex2F) vertexTexCoord2FAt: (GLsizei) index {
//	return [self vertexTexCoord2FAt: index forTextureUnit: 0];
//}
//
//-(void) setVertexTexCoord2F: (ccTex2F) aTex2F at: (GLsizei) index {
//	[self setVertexTexCoord2F: aTex2F at: index forTextureUnit: 0];
//}
//
//-(GLushort) vertexIndexAt: (GLsizei) index {
//	return mesh ? [mesh vertexIndexAt: index] : 0;
//}
//
//-(void) setVertexIndex: (GLushort) vertexIndex at: (GLsizei) index {
//	[mesh setVertexIndex: vertexIndex at: index];
//}
//
//@end


#pragma mark -
#pragma mark CC3LineNode
																								
CC3LineNode::CC3LineNode()
{
}

CC3LineNode::~CC3LineNode()
{
}

GLfloat CC3LineNode::getLineWidth()
{
	return lineWidth;
}

void CC3LineNode::setLineWidth(GLfloat newValue)
{
	lineWidth = newValue;
}
																								
GLenum CC3LineNode::getPerformanceHint()
{
	return performanceHint;
}

void CC3LineNode::setPerformanceHint(GLenum newValue)
{
	performanceHint = newValue;
}
																								
bool CC3LineNode::getShouldSmoothLines()
{
	return shouldSmoothLines;
}

void CC3LineNode::setShouldSmoothLines(bool newValue)
{
	shouldSmoothLines = newValue;
}
																								
void CC3LineNode::configureLineProperties()
{
	CC3OpenGLES11Engine* gles11Engine = CC3OpenGLES11Engine::engine();
	gles11Engine->getState()->getLineWidth()->setValue(lineWidth);
	gles11Engine->getServerCapabilities()->getLineSmooth()->setValue(shouldSmoothLines);
	gles11Engine->getHints()->getLineSmooth()->setValue(performanceHint);
}
																								
void CC3LineNode::configureDrawingParameters()
{
	CC3MeshNode::configureDrawingParameters();
	configureLineProperties();
}
																								
bool CC3LineNode::initWithTag(GLuint aTag, const char* aName)
{
	if ( CC3Node::initWithTag(aTag, aName) ) 
	{
		lineWidth = 1.0f;
		shouldSmoothLines = false;
		performanceHint = GL_DONT_CARE;
		
		return true;
	}
	
	return false;
}
																								
void CC3LineNode::populateFrom(CC3LineNode* another)
{
	CC3MeshNode::populateFrom(another);
	
	lineWidth = another->getLineWidth();
	shouldSmoothLines = another->getShouldSmoothLines();
	performanceHint = another->getPerformanceHint();
}

//@interface CC3LineNode (TemplateMethods)
//-(void) configureLineProperties;
//@end
//
//
//@implementation CC3LineNode
//
//@synthesize lineWidth, shouldSmoothLines, performanceHint;
//
//
//#pragma mark Allocation and initialization
//
//-(id) initWithTag: (GLuint) aTag withName: (NSString*) aName {
//	if ( (self = [super initWithTag: aTag withName: aName]) ) {
//		lineWidth = 1.0f;
//		shouldSmoothLines = NO;
//		performanceHint = GL_DONT_CARE;
//	}
//	return self;
//}
//
//// Template method that populates this instance from the specified other instance.
//// This method is invoked automatically during object copying via the copyWithZone: method.
//-(void) populateFrom: (CC3LineNode*) another {
//	[super populateFrom: another];
//	
//	lineWidth = another.lineWidth;
//	shouldSmoothLines = another.shouldSmoothLines;
//	performanceHint = another.performanceHint;
//}
//
//
//#pragma mark Drawing
//
///** Overridden to set the line properties in addition to other configuration. */
//-(void) configureDrawingParameters {
//	[super configureDrawingParameters];
//	[self configureLineProperties];
//}
//
//-(void) configureLineProperties {
//	CC3OpenGLES11Engine* gles11Engine = [CC3OpenGLES11Engine engine];
//	gles11Engine.state.lineWidth.value = lineWidth;
//	gles11Engine.serverCapabilities.lineSmooth.value = shouldSmoothLines;
//	gles11Engine.hints.lineSmooth.value = performanceHint;
//}
//
//@end


#pragma mark -
#pragma mark CC3WireframeBoundingBoxNode
																								
CC3WireframeBoundingBoxNode::CC3WireframeBoundingBoxNode()
{
}

CC3WireframeBoundingBoxNode::~CC3WireframeBoundingBoxNode()
{
}
																								
CC3WireframeBoundingBoxNode* CC3WireframeBoundingBoxNode::nodeWithName(const char* aName)
{
	CC3WireframeBoundingBoxNode* pNode = new CC3WireframeBoundingBoxNode;
	if (pNode) 
	{
		pNode->initWithName(aName);
		pNode->autorelease();
	}
	
	return pNode;
}
																								
void CC3WireframeBoundingBoxNode::setShouldAlwaysMeasureParentBoundingBox(bool newValue)
{
	shouldAlwaysMeasureParentBoundingBox = newValue;
}

bool CC3WireframeBoundingBoxNode::getShouldAlwaysMeasureParentBoundingBox()
{
	return shouldAlwaysMeasureParentBoundingBox;
}
																								
void CC3WireframeBoundingBoxNode::updateFromParentBoundingBoxWithVisitor(CC3NodeUpdatingVisitor* visitor)
{
	CC3BoundingBox pbb = getParentBoundingBox();
	setVertexLocation(cc3v(pbb.minimum.x, pbb.minimum.y, pbb.minimum.z), 0);
	setVertexLocation(cc3v(pbb.minimum.x, pbb.minimum.y, pbb.maximum.z), 1);
	setVertexLocation(cc3v(pbb.minimum.x, pbb.maximum.y, pbb.minimum.z), 2);
	setVertexLocation(cc3v(pbb.minimum.x, pbb.maximum.y, pbb.maximum.z), 3);
	setVertexLocation(cc3v(pbb.maximum.x, pbb.minimum.y, pbb.minimum.z), 4);
	setVertexLocation(cc3v(pbb.maximum.x, pbb.minimum.y, pbb.maximum.z), 5);
	setVertexLocation(cc3v(pbb.maximum.x, pbb.maximum.y, pbb.minimum.z), 6);
	setVertexLocation(cc3v(pbb.maximum.x, pbb.maximum.y, pbb.maximum.z), 7);
}
																								
CC3BoundingBox CC3WireframeBoundingBoxNode::getParentBoundingBox()
{
	return parent ? parent->getBoundingBox() : kCC3BoundingBoxZero;
}
																								
CC3BoundingBox CC3WireframeBoundingBoxNode::getLocalContentBoundingBox()
{
	return kCC3BoundingBoxNull;
}
																								
CC3BoundingBox CC3WireframeBoundingBoxNode::getGlobalLocalContentBoundingBox()
{
	return kCC3BoundingBoxNull;
}
																								
bool CC3WireframeBoundingBoxNode::getShouldIncludeInDeepCopy()
{
	return false;
}
																								
bool CC3WireframeBoundingBoxNode::getShouldDrawDescriptor()
{
	return true;
}
																								
void CC3WireframeBoundingBoxNode::setShouldDrawDescriptor(bool shouldDraw)
{
}
																								
bool CC3WireframeBoundingBoxNode::getShouldDrawWireframeBox()
{
	return true;
}

void CC3WireframeBoundingBoxNode::setShouldDrawWireframeBox(bool shouldDraw)
{
}
																								
bool CC3WireframeBoundingBoxNode::getShouldDrawLocalContentWireframeBox()
{
	return true;
}

void CC3WireframeBoundingBoxNode::setShouldDrawLocalContentWireframeBox(bool newValue)
{
}
																								
bool CC3WireframeBoundingBoxNode::getIsTouchable()
{
	return isTouchEnabled;
}
																								
bool CC3WireframeBoundingBoxNode::initWithTag(GLuint aTag, const char* aName)
{
	if ( CC3LineNode::initWithTag(aTag, aName) ) 
	{
		shouldAlwaysMeasureParentBoundingBox = false;
		
		return true;
	}
	
	return false;
}
																								
void CC3WireframeBoundingBoxNode::populateFrom(CC3WireframeBoundingBoxNode* another)
{
	CC3LineNode::populateFrom(another);
	
	shouldAlwaysMeasureParentBoundingBox = another->getShouldAlwaysMeasureParentBoundingBox();
}
																								
void CC3WireframeBoundingBoxNode::updateAfterTransform(CC3NodeUpdatingVisitor* visitor)
{
	if (getShouldAlwaysMeasureParentBoundingBox()) 
	{
		updateFromParentBoundingBoxWithVisitor(visitor);
	}
}

//@interface CC3WireframeBoundingBoxNode (TemplateMethods)
//-(void) updateFromParentBoundingBoxWithVisitor: (CC3NodeUpdatingVisitor*) visitor;
//@property(nonatomic, readonly) CC3BoundingBox parentBoundingBox;
//@end
//
//@implementation CC3WireframeBoundingBoxNode
//
//@synthesize shouldAlwaysMeasureParentBoundingBox;
//
//-(CC3BoundingBox) localContentBoundingBox {
//	return kCC3BoundingBoxNull;
//}
//
//-(CC3BoundingBox) globalLocalContentBoundingBox {
//	return kCC3BoundingBoxNull;
//}
//
//-(BOOL) shouldIncludeInDeepCopy { return NO; }
//
//-(BOOL) shouldDrawDescriptor { return YES; }
//
//-(void) setShouldDrawDescriptor: (BOOL) shouldDraw {}
//
//-(BOOL) shouldDrawWireframeBox { return YES; }
//
//-(void) setShouldDrawWireframeBox: (BOOL) shouldDraw {}
//
//-(BOOL) shouldDrawLocalContentWireframeBox { return YES; }
//
//-(void) setShouldDrawLocalContentWireframeBox: (BOOL) shouldDraw {}
//
//// Overridden so that not touchable unless specifically set as such
//-(BOOL) isTouchable {
//	return isTouchEnabled;
//}
//
//
//#pragma mark Allocation and initialization
//
//-(id) initWithTag: (GLuint) aTag withName: (NSString*) aName {
//	if ( (self = [super initWithTag: aTag withName: aName]) ) {
//		shouldAlwaysMeasureParentBoundingBox = NO;
//	}
//	return self;
//}
//
//// Template method that populates this instance from the specified other instance.
//// This method is invoked automatically during object copying via the copyWithZone: method.
//-(void) populateFrom: (CC3WireframeBoundingBoxNode*) another {
//	[super populateFrom: another];
//	
//	shouldAlwaysMeasureParentBoundingBox = another.shouldAlwaysMeasureParentBoundingBox;
//}
//
//
//#pragma mark Updating
//
///** If we should remeasure and update the bounding box dimensions, do so. */
//-(void) updateAfterTransform: (CC3NodeUpdatingVisitor*) visitor {
//	if (shouldAlwaysMeasureParentBoundingBox) {
//		[self updateFromParentBoundingBoxWithVisitor: visitor];
//	}
//}
//
///** Measures the bounding box of the parent node and updates the vertex locations. */
//-(void) updateFromParentBoundingBoxWithVisitor: (CC3NodeUpdatingVisitor*) visitor {
//	CC3BoundingBox pbb = self.parentBoundingBox;
//	[self setVertexLocation: cc3v(pbb.minimum.x, pbb.minimum.y, pbb.minimum.z) at: 0];
//	[self setVertexLocation: cc3v(pbb.minimum.x, pbb.minimum.y, pbb.maximum.z) at: 1];
//	[self setVertexLocation: cc3v(pbb.minimum.x, pbb.maximum.y, pbb.minimum.z) at: 2];
//	[self setVertexLocation: cc3v(pbb.minimum.x, pbb.maximum.y, pbb.maximum.z) at: 3];
//	[self setVertexLocation: cc3v(pbb.maximum.x, pbb.minimum.y, pbb.minimum.z) at: 4];
//	[self setVertexLocation: cc3v(pbb.maximum.x, pbb.minimum.y, pbb.maximum.z) at: 5];
//	[self setVertexLocation: cc3v(pbb.maximum.x, pbb.maximum.y, pbb.minimum.z) at: 6];
//	[self setVertexLocation: cc3v(pbb.maximum.x, pbb.maximum.y, pbb.maximum.z) at: 7];
//}
//
///** Retrieve the parent bounding box, or return the zero bounding box if no parent. */
//-(CC3BoundingBox) parentBoundingBox {
//	return parent ? parent.boundingBox : kCC3BoundingBoxZero;
//}
//
//@end


#pragma mark -
#pragma mark CC3WireframeLocalContentBoundingBoxNode
																								
CC3WireframeLocalContentBoundingBoxNode::CC3WireframeLocalContentBoundingBoxNode()
{
}

CC3WireframeLocalContentBoundingBoxNode::~CC3WireframeLocalContentBoundingBoxNode()
{
}

CC3BoundingBox CC3WireframeLocalContentBoundingBoxNode::getParentBoundingBox()
{
	return (parent && parent->getHasLocalContent())
	? ((CC3LocalContentNode*)parent)->getLocalContentBoundingBox()
	: kCC3BoundingBoxZero;
}

//@implementation CC3WireframeLocalContentBoundingBoxNode
//
///** Override to return the parent's local content bounding box */
//-(CC3BoundingBox) parentBoundingBox {
//	return (parent && parent.hasLocalContent)
//				? ((CC3LocalContentNode*)parent).localContentBoundingBox
//				: kCC3BoundingBoxZero;
//}
//
//@end


#pragma mark -
#pragma mark CC3PlaneNode
																								
CC3PlaneNode::CC3PlaneNode()
{
}

CC3PlaneNode::~CC3PlaneNode()
{
}

CC3PlaneNode* CC3PlaneNode::nodeWithName(const char* aName)
{
	CC3PlaneNode* pNode = new CC3PlaneNode;
	if (pNode) 
	{
		pNode->initWithName(aName);
		pNode->autorelease();
	}
	
	return pNode;
}

CC3Plane CC3PlaneNode::getPlane()
{
	CC3VertexArrayMesh* vam = (CC3VertexArrayMesh*)getMesh();
	CC3BoundingBox bb = vam->getVertexLocations()->getBoundingBox();
	
	// Get three points on the plane by using three corners of the mesh bounding box.
	CC3Vector p1 = bb.minimum;
	CC3Vector p2 = bb.maximum;
	CC3Vector p3 = bb.minimum;
	p3.x = bb.maximum.x;
	
	// Transform these points.
	p1 = getTransformMatrix()->transformLocation(p1);
	p2 = getTransformMatrix()->transformLocation(p2);
	p3 = getTransformMatrix()->transformLocation(p3);
	
	// Create and return a plane from these points.
	return CC3PlaneFromPoints(p1, p2, p3);
}

//@implementation CC3PlaneNode
//
//-(CC3Plane) plane {
//	CC3VertexArrayMesh* vam = (CC3VertexArrayMesh*)self.mesh;
//	CC3BoundingBox bb = vam.vertexLocations.boundingBox;
//	
//	// Get three points on the plane by using three corners of the mesh bounding box.
//	CC3Vector p1 = bb.minimum;
//	CC3Vector p2 = bb.maximum;
//	CC3Vector p3 = bb.minimum;
//	p3.x = bb.maximum.x;
//
//	// Transform these points.
//	p1 = [self.transformMatrix transformLocation: p1];
//	p2 = [self.transformMatrix transformLocation: p2];
//	p3 = [self.transformMatrix transformLocation: p3];
//
//	// Create and return a plane from these points.
//	return CC3PlaneFromPoints(p1, p2, p3);
//}
//
//@end


#pragma mark -
#pragma mark CC3BoxNode

//@implementation CC3BoxNode
//@end

CC3BoxNode* CC3BoxNode::nodeWithName(const char* aName)
{
	CC3BoxNode* pNode = new CC3BoxNode;
	if (pNode) 
	{
		pNode->initWithName(aName);
		pNode->autorelease();
	}
	return pNode;
}




